﻿using Application.Users.Interfaces;
using Application.Users.Models;
using Application.Users.Queries.GetUser;
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using Domain.Entities;
using Domain.ValueObjects;
using Infrastructure.Data.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Shared.Crypto;

namespace Infrastructure.Repositories;

public class UserReadRepository : IUserReadRepository
{
    private readonly AppDbContext _context;
    private readonly IPasswordHash _passwordHash;
    private readonly ILogger _logger;

    public UserReadRepository(AppDbContext context, IPasswordHash passwordHash, ILogger<UserReadRepository> logger)
    {
        this._context = context;
        _passwordHash = passwordHash;
        _logger = logger;
    }

    /// <summary>
    /// 通过id获取用户信息
    /// </summary>
    /// <param name="id"></param>
    /// <param name="ct"></param>
    /// <returns></returns>
    public async Task<IReadOnlyCollection<UserItem>?> GetByIdAsync(ICollection<Guid> id, CancellationToken ct = default)
    {
        _logger.LogInformation("用户查询{id}",id);
        return await _context.Users
            .Where(u => id.Contains(u.Id))
            .Select(u => new UserItem
            {
                Id = u.Id,
                UserName = u.NickName.Value,
                PhoneNumber = u.PhoneNumber.Value,
                CreateTime = u.CreateTime,
                CollegeName = u.AcademicInfo!.CollegeName,
                MajorName = u.AcademicInfo.MajorName,
            }).ToListAsync(ct);
    }

    /// <summary>
    /// 用户验证
    /// </summary>
    /// <param name="phoneNumber"></param>
    /// <param name="password"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<UserItem?> AuthenticateByPhoneNumberAsync(PhoneNumber phoneNumber, string password, CancellationToken cancellationToken = default)
    {
        string hashPassword = _passwordHash.Hash(password);
        var user = await _context.Users
        .Where(u => u.PhoneNumber.Equals(phoneNumber) && u.IsDeleted == false)
        .Select(u => new
        {
            u.Id,
            u.PhoneNumber,
            u.PasswordHash,
            u.NickName,
        })
        .FirstOrDefaultAsync(cancellationToken) ?? throw new Exception("todo //改为自定义异常");

        if (_passwordHash.Verify(user.PasswordHash, hashPassword))
            return new UserItem()
            {
                PhoneNumber = phoneNumber,
                Id = user.Id,
                UserName = user.NickName
            };
        throw new Exception("todo //改为自定义异常");
    }

    public async Task<IReadOnlyCollection<GetUserModel>> GetUsersAsync(ISpecification<User> spec, CancellationToken cancellationToken = default)
    {
        var query = _context.Users.AsQueryable();

        query = SpecificationEvaluator.Default.GetQuery(query, spec)
            .Where(x => x.IsDeleted == false)
            .AsNoTracking();

        var users = await query.Select(x => new GetUserModel()
        {
            CollegeName = x.AcademicInfo!.CollegeName,
            Id = x.Id,
            CreateTime = x.CreateTime,
            UpdateTime = x.UpdateTime,
            MajorName = x.AcademicInfo.MajorName,
            PhoneNumber = x.PhoneNumber.Value,
            UserName = x.NickName.Value,
        }).ToListAsync(cancellationToken);
        return users.AsReadOnly();
    }

}
